﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;
using System.Xml;
using System.Xml.Linq;

namespace NekoVampire.TwitterClient.Twitter
{
    /// <summary>Twitterへのアクセスを提供する</summary>
    public sealed class AccessTwitter
    {
        #region 変数の定義
        private const string API = "http://twitter.com/";
        private const string SOURCE = "Twite";

        private NetworkCredential Credentials;

        public string UserName
        {
            get
            {
                return Credentials.UserName;
            }
            set
            {
                Credentials.UserName = value;
            }
        }

        public string Password
        {
            get
            {
                return Credentials.Password;
            }
            set
            {
                Credentials.Password = value;
            }
        }
        #endregion

        private static readonly AccessTwitter instance = new AccessTwitter();
        
        /// <summary>AccessTwitterのインスタンス</summary>
        public static AccessTwitter Requester
        {
            get
            {
                return instance;
            }
        }

        #region コンストラクタ
        /// <summary>AccessTwitterのコンストラクタ</summary>
        private AccessTwitter()
        {
            Credentials = new NetworkCredential();
        }

        /*
        /// <summary>AccessTwitterのコンストラクタ</summary>
        /// <param name="userName">ユーザーID</param>
        /// <param name="password">パスワード</param>
        private AccessTwitter(string userName, string password)
        {
            Credentials = new NetworkCredential(userName, password);
        }
         */
        #endregion
        
        #region GetTimeLine
        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大20件を取得する。</summary>
        public Stream GetTimeLine()
        {
            string url = API + "statuses/friends_timeline.xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大20件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        public Stream GetTimeLine(int page)
        {
            string url = API + "statuses/friends_timeline.xml?page=" + page;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大count(=200まで)件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="count">取得件数</param>
        public Stream GetTimeLine(int page,int count)
        {
            string url = API + "statuses/friends_timeline.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大20件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        public Stream GetTimeLine(int page, DateTime since)
        {
            string url = API + "statuses/friends_timeline.xml?page=" + page;

            try
            {
                return GetStreamGETHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスからcount(=200まで)件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        /// <param name="count">取得件数</param>
        public Stream GetTimeLine(int page, int count, DateTime since)
        {
            string url = API + "statuses/friends_timeline.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamGETHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        #region GetReplies
        /// <summary>自分に対する返信(冒頭が @ユーザ名 で始まるステータス)の一覧を取得する (最大20件)</summary>
        public Stream GetReplies()
        {
            string url = API + "statuses/replies.xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分に対する返信(冒頭が @ユーザ名 で始まるステータス)の一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        public Stream GetReplies(int page)
        {
            string url = API + "statuses/replies.xml?page=" + page;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分に対する返信(冒頭が @ユーザ名 で始まるステータス)の一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="count">取得件数</param>
        public Stream GetReplies(int page, int count)
        {
            string url = API + "statuses/replies.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分に対する返信(冒頭が @ユーザ名 で始まるステータス)の一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        public Stream GetReplies(int page, DateTime since)
        {
            string url = API + "statuses/replies.xml?page=" + page;

            try
            {
                return GetStreamGETHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分に対する返信(冒頭が @ユーザ名 で始まるステータス)の一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        /// <param name="count">取得件数</param>
        public Stream GetReplies(int page, int count, DateTime since)
        {
            string url = API + "statuses/replies.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamGETHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion
        #region GetTimeLine
        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大20件を取得する。</summary>
        public Stream GetUserTimeLine(string user)
        {
            string url = API + "statuses/user_timeline/" + user + ".xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大20件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        public Stream GetUserTimeLine(string user, int page)
        {
            string url = API + "statuses/user_timeline/" + user + ".xml?page=" + page;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大count(=200まで)件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="count">取得件数</param>
        public Stream GetUserTimeLine(string user, int page, int count)
        {
            string url = API + "statuses/user_timeline/" + user + ".xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスから最大20件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        public Stream GetUserTimeLine(string user, int page, DateTime since)
        {
            string url = API + "statuses/user_timeline/" + user + ".xml?page=" + page;

            try
            {
                return GetStreamGETHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分の friend の過去24時間以内に update されたステータスからcount(=200まで)件を取得する。</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        /// <param name="count">取得件数</param>
        public Stream GetUserTimeLine(string user, int page, int count, DateTime since)
        {
            string url = API + "statuses/user_timeline/" + user + ".xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamGETHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion


        /// <summary>自分のステータスを更新(update)する。</summary>
        /// <param name="status">ステータス</param>
        public Stream SendUpdate(string status)
        {
            string url = API + "statuses/update.xml";
            byte[] data = Encoding.ASCII.GetBytes(
                "status=" +
                HttpUtility.UrlEncode(status, Encoding.UTF8) +
                "&source=" +
                HttpUtility.UrlEncode(SOURCE, Encoding.UTF8));

            try
            {
                return GetStreamPOSTHttpRequest(url, data);
            }
            catch (Exception)
            {
                throw;
            }

        }

        /// <summary>自分のステータスを更新(update)する。</summary>
        /// <param name="status">ステータス</param>
        public Stream SendUpdate(string status, string inReplyToStatusId)
        {
            string url = API + "statuses/update.xml";
            byte[] data = Encoding.ASCII.GetBytes(
                "status=" +
                HttpUtility.UrlEncode(status, Encoding.UTF8) +
                "&in_reply_to_status_id=" +
                HttpUtility.UrlEncode(inReplyToStatusId, Encoding.UTF8) +
                "&source=" +
                HttpUtility.UrlEncode(SOURCE, Encoding.UTF8));

            try
            {
                return GetStreamPOSTHttpRequest(url, data);
            }
            catch (Exception)
            {
                throw;
            }

        }

        /// <summary>ステータスを削除する。</summary>
        /// <param name="id">ステータスID</param>
        public Stream DestroyStatus(string id)
        {
            string url = API + "statuses/destroy/" + id + ".xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream CreateFav(string id)
        {
            string url = API + "favorites/create/" + id + ".xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream DestroyFav(string id)
        {
            string url = API + "favorites/destroy/" + id + ".xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream ShowUser(string id)
        {
            string url = API + "users/show/" + id + ".xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream CreateFriend(string user)
        {
            string url = API + "friendships/create/" + user + ".xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream DestroyFriend(string user)
        {
            string url = API + "friendships/destroy/" + user + ".xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream ExistFriend(string user)
        {
            string url = API + "friendships/exists.xml"
                + "?user_a=" + this.UserName
                + "&user_b=" + user;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream ExistFriend(string user_a, string user_b)
        {
            string url = API + "friendships/exists.xml"
                + "?user_a=" + user_a
                + "&user_b=" + user_b;

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream GetFriends()
        {
            string url = API + "friends/ids.xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream GetFriends(string id)
        {
            string url = API + "friends/ids/" + id + ".xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream GetFollowers()
        {
            string url = API + "followers/ids.xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Stream GetFollowers(string id)
        {
            string url = API + "followers/ids/" + id + ".xml";

            try
            {
                return GetStreamGETHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        #region GetDM
        /// <summary>自分宛てのダイレクトメッセージの一覧を取得する (最大20件)</summary>
        public Stream GetDM()
        {
            string url = API + "direct_messages.xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分宛てのダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        public Stream GetDM(int page)
        {
            string url = API + "direct_messages.xml?page=" + page;

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分宛てのダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="count">取得件数</param>
        public Stream GetDM(int page, int count)
        {
            string url = API + "direct_messages.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分宛てのダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        public Stream GetDM(int page, DateTime since)
        {
            string url = API + "direct_messages.xml?page=" + page;

            try
            {
                return GetStreamPOSTHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分宛てのダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        /// <param name="count">取得件数</param>
        public Stream GetDM(int page, int count, DateTime since)
        {
            string url = API + "direct_messages.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamPOSTHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        #region GetDMSent
        /// <summary>自分が送信したダイレクトメッセージの一覧を取得する (最大20件)</summary>
        public Stream GetDMSent()
        {
            string url = API + "direct_messages/sent.xml";

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分が送信したダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        public Stream GetDMSent(int page)
        {
            string url = API + "direct_messages/sent.xml?page=" + page;

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分が送信したダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="count">取得件数</param>
        public Stream GetDMSent(int page, int count)
        {
            string url = API + "direct_messages/sent.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamPOSTHttpRequest(url);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分が送信したダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        public Stream GetDMSent(int page, DateTime since)
        {
            string url = API + "direct_messages/sent.xml?page=" + page;

            try
            {
                return GetStreamPOSTHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>自分が送信したダイレクトメッセージの一覧を取得する (最大20件)</summary>
        /// <param name="page">ページ番号</param>
        /// <param name="since">日時</param>
        /// <param name="count">取得件数</param>
        public Stream GetDMSent(int page, int count, DateTime since)
        {
            string url = API + "direct_messages/sent.xml?page=" + page + "&count=" + count;

            try
            {
                return GetStreamPOSTHttpRequest(url, since);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        /// <summary>ダイレクトメッセージを送信する。</summary>
        /// <param name="status">ステータス</param>
        public Stream SendDM(string UserID, string text)
        {
            string url = API + "direct_messages/new.xml";
            byte[] data = Encoding.ASCII.GetBytes(
                "user=" +
                HttpUtility.UrlEncode(UserID, Encoding.UTF8) +
                "&text=" +
                HttpUtility.UrlEncode(text, Encoding.UTF8) +
                "&source=" +
                HttpUtility.UrlEncode(SOURCE, Encoding.UTF8));

            try
            {
                return GetStreamPOSTHttpRequest(url, data);
            }
            catch (Exception)
            {
                throw;
            }
        }

        #region GetStream～
        protected Stream GetStreamGETHttpRequest(string url)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "GET";
            req.Credentials = Credentials;

            try
            {
                WebResponse responce = req.GetResponse();
                return responce.GetResponseStream();
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected Stream GetStreamGETHttpRequest(string url, DateTime since)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "GET";
            req.Credentials = Credentials;
            if (since != null)
            {
                req.IfModifiedSince = since;
            }

            try
            {
                WebResponse responce = req.GetResponse();
                return responce.GetResponseStream();
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected Stream GetStreamPOSTHttpRequest(string url)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "POST";
            req.Credentials = Credentials;

            try
            {
                WebResponse responce = req.GetResponse();
                return responce.GetResponseStream();
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected Stream GetStreamPOSTHttpRequest(string url, DateTime since)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "POST";
            req.Credentials = Credentials;
            if (since != null)
            {
                req.IfModifiedSince = since;
            }

            try
            {
                WebResponse responce = req.GetResponse();
                return responce.GetResponseStream();
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected Stream GetStreamPOSTHttpRequest(string url, byte[] data)
        {

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "POST";
            req.Credentials = Credentials;

            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = data.Length;

            req.ServicePoint.Expect100Continue = false;
            using (Stream reqStream = req.GetRequestStream())
            {
                try
                {
                    reqStream.Write(data, 0, data.Length);
                    reqStream.Close();
                }
                catch (WebException)
                {
                    throw;
                }
            }

            WebResponse responce;
            try
            {
                responce = req.GetResponse();
                return responce.GetResponseStream();
            }
            catch (WebException)
            {
                throw;
            }
        }
        #endregion
    }
}
